<?php

namespace StudyBuddy\Template;

use \StudyBuddy\Math\Math;

class Template {

    /**
     * Can override this static method in concrete template
     * In accepts array of $vars by reference
     * This method can modify actual values
     * or template variables
     * before the variables are actually used
     * in template
     *
     * @param array $vars
     */
    protected static function func(&$vars) {
        
    }

    /**
     * Flag indicates that
     * template should skip
     * calling the $func function
     *
     *
     * @var bool
     */
    protected static $skip = false;

    protected static function translate($s, array $vars = null) {
        if (isset($_SESSION) && !empty($_SESSION['Translator'])) {
            return $_SESSION['Translator']->get($s, $vars, $s);
        }

        return $s;
    }

    /**
     * Parse template, using input $aVars array of replacement
     * variables to be used in the vsprintf() function
     *
     * @param array $aVars
     *
     * @param bool $merge if true will apply default values
     * as well as making sure the elements or input
     * array are in the correct order. This is very important
     * if you not sure that array of values you passing has
     * named elements in correct order.
     * If you are 100% sure that elements are in correct order
     * then set this to false to save function call
     *
     * @param Closure $func callback function to be applied
     * to input array. The callback anonymous function MUST
     * accept input array by reference and perform some
     * operations on the actual array.
     */
    public static function parse(array $aVars, $merge = true, $func = null) {

        /**
         * ORDER IS IMPORTANT:
         * Closure functions applied first, then static function "func"
         *
         * Apply callback to array if
         * callback was passed here
         * callback MUST accept array by reference
         * so that it can modify actual values in aVars
         *
         * The callback should be applied first,
         * so that in case the template also has a function,
         * it would be possible to use callback to add
         * elements to the result array.
         *
         * This is useful when we have a cursor - a result
         * of database select but also need to inject
         * extra element(s) to the array of item which
         * are not present in the database
         */
        if (null !== $func) {

            $func($aVars);
        }

        /**
         * A template may contain hard coded static property $func
         *
         * If it does then input array will be run through
         * that $func function
         * it MUST accept array by reference
         * and modify actual array value
         *
         */
        if (false === static::$skip) {

            static::func($aVars);
        }

        if ($merge) {

            $aVars = \array_merge(static::$vars, $aVars);
        }

        $begin = $end = $t = '';

        if (true === STUDYBUDDY_DEBUG) {
            $t = '  ';
            $templateName = get_called_class();
            $begin = sprintf("\n$t<!-- Template %s -->\n", $templateName);
            $end = sprintf("\n$t<!-- // Template %s -->\n", $templateName);
        }

        $ret = \vsprintf(static::$tpl, $aVars);

        return $begin . $t . $ret . $end;
    }

    /**
     * @todo template may contain $loop static
     * function, if it does then use it on
     * passed array
     *
     * @param mixed $a could be array or object of type Iterator
     * @param bool $merge
     * @param Closure $func if passed, this callback function
     * will be passed to each element's parse() function
     *
     * @throws InvalidArgumentException if $a is not array and not Iterator
     */
    public static function loop($a, $merge = true, $func = null) {
        $begin = $end = '';
        /**
         * Throw exception if Iterator is not
         * an array and not instance of iterator
         */
        if (!is_array($a) && (!is_object($a) || !($a instanceof \Iterator))) {
            $err = 'Param $a (first param passed to loop() must be array of object instance of Iterator was: ' . gettype($a);

            throw new \InvalidArgumentException($err);
        }



        /**
         * Cannot just declare this $s as static inside the
         * method
         * because then it remembers this value
         * even between parsing multiple times
         * Instead we must recursively pass $s to itself
         */
        $s = '';
        foreach ($a as $aVars) {
            if (is_string($aVars)) {
                $vars = array($aVars);
                d('aVars now: ' . print_r($vars, 1));
            } else {
                $vars = $aVars;
            } //debug($vars, false);
            // equation
            if (isset($vars['b'])) {
                $math = new Math();
                $vars['b'] = $math->mathfilter($vars['b'], 13, 'http://' . $_SERVER['HTTP_HOST'] . '/w/img/equation/');
            }
            if (isset($vars['intro'])) {
                $math = new Math();
                $vars['intro'] = $math->mathfilter($vars['intro'], 13, 'http://' . $_SERVER['HTTP_HOST'] . '/w/img/equation/');
            }
            $s .= static::parse($vars, $merge, $func);
        }

        if (true === STUDYBUDDY_DEBUG) {
            $templateName = get_called_class();
            $begin = sprintf("\n<!-- BEGIN LOOP in template: %s -->\n", $templateName);
            $end = sprintf("\n<!-- // END  LOOP in template: %s -->\n", $templateName);
        }

        return $begin . $s . $end;
    }

    /**
     * Static getter for vars array
     *
     * @asKeys bool if true then return array of
     * placeholder names. This type of array is useful for
     * passing it to mongocollection->find() 2nd param
     * to hint which field we need to select.
     *
     * @return array of $vars from template OR if
     * asKeys is true then returns array of placeholder names
     */
    public static function getVars($asKeys = false) {
        if (isset(static::$vars)) {
            $ret = static::$vars;
            if ($asKeys) {
                return array_keys($ret);
            }

            return $ret;
        }

        return array();
    }

    /**
     * Static getter for $tpl
     *
     * @return string $tpl template
     */
    public static function getTemplate() {
        return static::$tpl;
    }

    /**
     * Get array of unparsed template
     * and default vars
     *
     * It may be very helpful in case of Ajax to load
     * the template into browser and do the
     * vsprinf(tpl, vars) in javascript
     * using the sprintf for javascript package
     *
     * @see http://www.diveintojavascript.com/projects/javascript-sprintf
     *
     * @return array with keys 'tpl' and 'vars' representing
     * the template on which this was called
     */
    public static function get() {
        return array('tpl' => static::$tpl, 'vars' => static::$vars);
    }

}
